package org.gdstash.util;

import org.gdstash.ui.GDDialog;
import org.gdstash.ui.GDLogDialog;

import javax.swing.*;
import java.awt.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedList;
import java.util.List;

public class GDLog {
    private List<LogEntry> list;
    private boolean error;
    private int errorLow;
    private int errorMedium;
    private int errorHigh;

    public GDLog() {
        this.list = new LinkedList<LogEntry>();
        this.error = false;
        this.errorLow = 0;
        this.errorMedium = 0;
        this.errorHigh = 0;
    }

    public static String getStackTrace(final Throwable ex) {
        final StringWriter sw = new StringWriter();
        final PrintWriter pw = new PrintWriter(sw, true);
        ex.printStackTrace(pw);
        return sw.getBuffer().toString();
    }

    private void addException(final Throwable ex, final MessageType type, final Severity severity) {
        this.list.add(new LogEntry(ex, type, severity));
        if (type == MessageType.Error) {
            this.error = true;
            if (severity == Severity.Low) {
                ++this.errorLow;
            }
            if (severity == Severity.Medium) {
                ++this.errorMedium;
            }
            if (severity == Severity.High) {
                ++this.errorHigh;
            }
        }
    }

    private void addMessage(final String s, final MessageType type, final Severity severity) {
        this.list.add(new LogEntry(s, type, severity));
        if (type == MessageType.Error) {
            this.error = true;
            if (severity == Severity.Low) {
                ++this.errorLow;
            }
            if (severity == Severity.Medium) {
                ++this.errorMedium;
            }
            if (severity == Severity.High) {
                ++this.errorHigh;
            }
        }
    }

    public void addMessage(final String s, final MessageType type) {
        this.addMessage(s, type, Severity.Medium);
    }

    public void addMessage(final String s) {
        this.addMessage(s, MessageType.Success);
    }

    public void addSuccess(final String s) {
        this.addMessage(s, MessageType.Success);
    }

    public void addInfo(final String s) {
        this.addMessage(s, MessageType.Info);
    }

    public void addWarning(final String s) {
        this.addMessage(s, MessageType.Warning);
    }

    public void addError(final String s) {
        this.addMessage(s, MessageType.Error, Severity.Medium);
    }

    public void addLowError(final String s) {
        this.addMessage(s, MessageType.Error, Severity.Low);
    }

    public void addHighError(final String s) {
        this.addMessage(s, MessageType.Error, Severity.High);
    }

    public void addWarning(final Throwable ex) {
        this.addException(ex, MessageType.Warning, Severity.Medium);
    }

    public void addError(final Throwable ex) {
        this.addException(ex, MessageType.Error, Severity.Medium);
    }

    public void addLowError(final Throwable ex) {
        this.addException(ex, MessageType.Error, Severity.Low);
    }

    public void addHighError(final Throwable ex) {
        this.addException(ex, MessageType.Error, Severity.High);
    }

    public void addLog(final GDLog log) {
        this.list.addAll(log.list);
        if (log.containsErrors()) {
            this.error = true;
            this.errorLow += log.getNumberLowErrors();
            this.errorMedium += log.getNumberMediumErrors();
            this.errorHigh += log.getNumberHighErrors();
        }
    }

    public boolean containsErrors() {
        return this.error;
    }

    public boolean containSevereErrors() {
        return this.getNumberLowErrors() > 10 || this.getNumberMediumErrors() > 0 || this.getNumberHighErrors() > 0;
    }

    public boolean containsMessages() {
        return !this.list.isEmpty();
    }

    public List<LogEntry> getLog() {
        return this.list;
    }

    public int getNumberLowErrors() {
        return this.errorLow;
    }

    public int getNumberMediumErrors() {
        return this.errorMedium;
    }

    public int getNumberHighErrors() {
        return this.errorHigh;
    }

    public void clear() {
        this.list.clear();
        this.error = false;
        this.errorLow = 0;
        this.errorMedium = 0;
        this.errorHigh = 0;
    }

    public void showLog(final Component owner, final String message, final MessageType type) {
        final JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(owner);
        final GDLogDialog dlg = new GDLogDialog(frame, message, type, this);
        this.clear();
    }

    public void showLog(final Component owner, final String error) {
        this.showLog(owner, "", MessageType.Error, error, false, true);
    }

    public void showLog(final Component owner, final String success, final MessageType type, final String error) {
        this.showLog(owner, success, type, error, false, false);
    }

    public void showLog(final Component owner, final String success, final MessageType type, final String error, final boolean showEmpty) {
        this.showLog(owner, success, type, error, showEmpty, false);
    }

    public void showLog(final Component owner, final String success, final MessageType type, final String error, final boolean showEmpty, final boolean errorOnly) {
        final JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(owner);
        if (this.containsErrors()) {
            final GDLogDialog gdLogDialog = new GDLogDialog(frame, error, MessageType.Error, this);
        } else if (this.containsMessages()) {
            if (!errorOnly) {
                final GDLogDialog gdLogDialog2 = new GDLogDialog(frame, success, type, this);
            }
        } else if (showEmpty) {
            final GDDialog dialog = new GDDialog(success, 1, frame, GDMsgFormatter.getString(GDMsgFormatter.rbUI, "SUCCESS"), true);
            dialog.setVisible(true);
        }
        this.clear();
    }

    public void showSevereLog(final Component owner, final String success, final MessageType type, final String error, final boolean showEmpty, final boolean errorOnly) {
        final JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(owner);
        if (this.containSevereErrors()) {
            final GDLogDialog gdLogDialog = new GDLogDialog(frame, error, MessageType.Error, this);
        } else if (this.containsMessages()) {
            if (!errorOnly) {
                final GDLogDialog gdLogDialog2 = new GDLogDialog(frame, success, type, this);
            }
        } else if (showEmpty) {
            final GDDialog dialog = new GDDialog(success, 1, frame, GDMsgFormatter.getString(GDMsgFormatter.rbUI, "SUCCESS"), true);
            dialog.setVisible(true);
        }
        this.clear();
    }

    public enum MessageType {
        Success,
        Info,
        Warning,
        Error;
    }

    public enum Severity {
        Low,
        Medium,
        High;
    }

    public static class LogEntry {
        public MessageType type;
        public Severity severity;
        public Throwable ex;
        public String str;

        public LogEntry(final String s) {
            this(s, MessageType.Success, Severity.Medium);
        }

        public LogEntry(final String s, final MessageType type, final Severity severity) {
            this.type = type;
            this.severity = severity;
            this.str = s;
            this.ex = null;
        }

        public LogEntry(final Exception ex) {
            this(ex, MessageType.Error, Severity.Medium);
        }

        public LogEntry(final Throwable ex, final MessageType type, final Severity severity) {
            this.type = type;
            this.severity = severity;
            this.ex = ex;
            if (ex.getMessage() == null || ex.getMessage().isEmpty()) {
                this.str = ex.toString();
            } else {
                this.str = ex.getMessage();
            }
            this.str = this.str + GDConstants.LINE_SEPARATOR + GDLog.getStackTrace(ex);
        }
    }
}
